"""
交易保护机制

提供各种交易保护功能，防止过度交易和误操作
"""

import logging
from datetime import datetime, timedelta
from typing import Dict, Optional

logger = logging.getLogger(__name__)


class TradeProtector:
    """
    交易保护器
    
    提供以下保护机制：
    - 持仓时间保护（防止秒买秒卖）
    - 重复买入冷静期（防止频繁交易）
    - 前高距离保护（条件B2）
    - 跌破幅度保护（条件C）
    """
    
    def __init__(self, config: dict):
        """
        初始化交易保护器
        
        Args:
            config: 保护配置字典，包含：
                - min_holding_minutes: 最小持仓时间（分钟）
                - repeat_buy_cooldown_hours: 重复买入冷静期（小时）
                - min_break_pct: 最小跌破幅度（%）
        """
        self.config = config
        self.min_holding_minutes = config.get('min_holding_minutes', 30)
        self.cooldown_hours = config.get('repeat_buy_cooldown_hours', 24)
        self.min_break_pct = config.get('min_break_pct', 0.5)
        
        # 记录最后买入时间（用于冷静期）
        self.last_buy_time: Dict[str, datetime] = {}
        
        # 记录已经输出过冷静期日志的股票（避免重复输出）
        self._cooldown_logged: Dict[str, bool] = {}
        
    def check_t1_rule(self, 
                     stock: str,
                     buy_time: datetime,
                     current_time: Optional[datetime] = None) -> bool:
        """
        检查T+1规则：当天买的股票当天不能卖
        
        Args:
            stock: 股票代码
            buy_time: 买入时间
            current_time: 当前时间（None则使用datetime.now()）
            
        Returns:
            bool: True表示可以卖出，False表示违反T+1规则不能卖
        """
        if current_time is None:
            current_time = datetime.now()
        
        # 比较日期（不比较时间）
        buy_date = buy_time.date()
        current_date = current_time.date()
        
        if buy_date == current_date:
            logger.debug(f"{stock} T+1限制：今天买入不能今天卖出（买入日期={buy_date}）")
            return False
        
        return True
    
    def check_holding_time(self, 
                          stock: str,
                          buy_time: datetime,
                          current_time: Optional[datetime] = None) -> bool:
        """
        检查是否满足最小持仓时间
        
        注意：此方法用于防止频繁交易，与T+1规则分开
        
        Args:
            stock: 股票代码
            buy_time: 买入时间
            current_time: 当前时间（None则使用datetime.now()）
            
        Returns:
            bool: True表示满足最小持仓时间，False表示不满足
        """
        if current_time is None:
            current_time = datetime.now()
        
        hold_minutes = (current_time - buy_time).total_seconds() / 60
        
        if hold_minutes < self.min_holding_minutes:
            logger.debug(f"{stock} 持仓时间{hold_minutes:.1f}分钟 < 最小持仓时间{self.min_holding_minutes}分钟")
            return False
        
        return True
    
    def check_repeat_buy_cooldown(self,
                                  stock: str,
                                  current_time: Optional[datetime] = None) -> bool:
        """
        检查是否在重复买入冷静期内
        
        Args:
            stock: 股票代码
            current_time: 当前时间（None则使用datetime.now()）
            
        Returns:
            bool: True表示可以买入，False表示在冷静期内
        """
        if stock not in self.last_buy_time:
            return True
        
        if current_time is None:
            current_time = datetime.now()
        
        last_buy = self.last_buy_time[stock]
        time_diff_hours = (current_time - last_buy).total_seconds() / 3600
        
        if time_diff_hours < self.cooldown_hours:
            remaining_hours = self.cooldown_hours - time_diff_hours
            
            # 只在第一次检测到冷静期时输出日志（避免重复输出）
            if not self._cooldown_logged.get(stock, False):
                logger.info(f"🔒 {stock} 进入{self.cooldown_hours}小时冷静期，距离下次可买入还有 {remaining_hours:.1f} 小时")
                logger.debug(f"   上次买入: {last_buy}, 当前时间: {current_time}, 时间差: {time_diff_hours:.2f}小时")
                self._cooldown_logged[stock] = True
            
            return False
        
        # 冷静期结束，清除日志标记，下次买入后重新记录
        self._cooldown_logged.pop(stock, None)
        return True
    
    def record_buy_time(self, stock: str, buy_time: Optional[datetime] = None):
        """
        记录买入时间
        
        Args:
            stock: 股票代码
            buy_time: 买入时间（None则使用datetime.now()）
        """
        if buy_time is None:
            buy_time = datetime.now()
        
        self.last_buy_time[stock] = buy_time
        # 清除冷静期日志标记，下次冷静期重新记录
        self._cooldown_logged.pop(stock, None)
        logger.debug(f"{stock} 记录买入时间: {buy_time}")
    
    def check_breakthrough_protection(self,
                                     current_price: float,
                                     previous_high: float,
                                     increase_pct: float,
                                     breakthrough_threshold: float = 2.0,
                                     increase_threshold: float = 2.0) -> bool:
        """
        检查前高距离保护（条件B2）
        
        Args:
            current_price: 当前价格
            previous_high: 前期高点
            increase_pct: 当日涨幅（%）
            breakthrough_threshold: 突破阈值（%），默认2%
            increase_threshold: 涨幅阈值（%），默认2%
            
        Returns:
            bool: True表示应该保护（不触发B2），False表示不保护
        """
        breakthrough_pct = (current_price - previous_high) / previous_high * 100
        
        # 如果刚突破前高2%以内，且涨幅≥2%，则保护
        if breakthrough_pct < breakthrough_threshold and increase_pct >= increase_threshold:
            logger.debug(f"前高距离保护生效: 突破{breakthrough_pct:.2f}% < {breakthrough_threshold}%, "
                        f"涨幅{increase_pct:.2f}% >= {increase_threshold}%")
            return True
        
        return False
    
    def check_break_amplitude(self,
                             current_price: float,
                             ma5: float) -> bool:
        """
        检查跌破幅度保护（条件C）
        
        Args:
            current_price: 当前价格
            ma5: 5日均线
            
        Returns:
            bool: True表示跌破幅度足够（可以触发C），False表示不足
        """
        if current_price >= ma5:
            return False  # 没有跌破
        
        break_pct = (ma5 - current_price) / ma5 * 100
        
        if break_pct < self.min_break_pct:
            logger.debug(f"跌破幅度{break_pct:.2f}% < 最小幅度{self.min_break_pct}%，不触发条件C")
            return False
        
        return True
    
    def should_trigger_sell_b2(self,
                              stock: str,
                              buy_time: datetime,
                              current_price: float,
                              previous_high: float,
                              increase_pct: float,
                              current_time: Optional[datetime] = None) -> bool:
        """
        综合判断是否应该触发条件B2
        
        Args:
            stock: 股票代码
            buy_time: 买入时间
            current_price: 当前价格
            previous_high: 前期高点
            increase_pct: 当日涨幅（%）
            current_time: 当前时间
            
        Returns:
            bool: True表示应该触发B2，False表示不触发
        """
        # 检查持仓时间保护
        if not self.check_holding_time(stock, buy_time, current_time):
            return False
        
        # 检查前高距离保护
        if self.check_breakthrough_protection(current_price, previous_high, increase_pct):
            return False
        
        return True
    
    def should_trigger_sell_c(self,
                             current_price: float,
                             ma5: float) -> bool:
        """
        判断是否应该触发条件C
        
        Args:
            current_price: 当前价格
            ma5: 5日均线
            
        Returns:
            bool: True表示应该触发C，False表示不触发
        """
        return self.check_break_amplitude(current_price, ma5)
    
    def clear_cooldown(self, stock: str):
        """
        清除指定股票的冷静期记录
        
        Args:
            stock: 股票代码
        """
        if stock in self.last_buy_time:
            del self.last_buy_time[stock]
            logger.debug(f"{stock} 冷静期记录已清除")
    
    def get_protection_status(self, stock: str) -> dict:
        """
        获取股票的保护状态信息
        
        Args:
            stock: 股票代码
            
        Returns:
            dict: 保护状态信息
        """
        status = {
            'stock': stock,
            'last_buy_time': self.last_buy_time.get(stock),
            'in_cooldown': not self.check_repeat_buy_cooldown(stock),
            'cooldown_hours': self.cooldown_hours,
            'min_holding_minutes': self.min_holding_minutes,
            'min_break_pct': self.min_break_pct
        }
        
        if status['last_buy_time']:
            time_since_buy = (datetime.now() - status['last_buy_time']).total_seconds() / 3600
            status['hours_since_buy'] = time_since_buy
            status['remaining_cooldown_hours'] = max(0, self.cooldown_hours - time_since_buy)
        
        return status

